package service.impl;

import java.util.ArrayList;
import java.util.List;

import service.CalculateRoadStatus;
import service.RoadService;
import service.VehicleMode;
import bean.Distance;
import bean.Graph;
import bean.Intersection;
import bean.Point;
import bean.Road;
import bean.RoadStatus;
import bean.Tile;
import bean.Vehicle;

import common.BeanUtils;

import dao.IntersectionDao;
import dao.RoadDao;
import dao.TileDao;
import dao.impl.StaticData;
import exception.DBException;

public class RoadServiceImpl implements RoadService {

	private RoadDao roadDao;
	private TileDao tileDao;
	private IntersectionDao intersectionDao;
	private Graph graph;
	private static VehicleMode randomMode;
	private static CalculateRoadStatus roadStatus;
	private StaticData staticData;

	@Override
	public boolean saveOrUpdate(List<Road> roads) throws DBException {
		boolean b = roadDao.update(roads);
		restoreDistance(roads.get(0).getPoint()[0].getArea().getId());
		return b;
	}

	@Override
	public List<Road> queryAllRoadsInArea(int area) throws DBException {
		return roadDao.selectAllByArea(area);
	}

	@Override
	public boolean removeRoadById(int id) throws DBException {
		Road road = roadDao.select(id);
		roadDao.delete(road);
		return true;
	}

	@Override
	public List<Distance> restoreDistance(int area) throws DBException {
		List<Road> roads = roadDao.selectAllByArea(area);
		if (roads == null || roads.size() == 0)
			return null;
		List<Distance> dis = new ArrayList<Distance>();
		for (Road road : roads) {
			Point[] points = road.getPoint();
			for (int i = 0; i < points.length - 1; i++) {
				Point p1 = points[i];
				Point p2 = points[i + 1];
				float distance = (float) Math.hypot(p1.getX() - p2.getX(),
						p1.getY() - p2.getY());
				dis.add(new Distance(p1, p2, distance));
			}
		}
		dis = roadDao.restoreDistance(dis, area);
		return dis;
	}

	@Override
	public List<Point> shortestPath(int area, Point start, Point end,
			boolean update) throws DBException {
		List<Distance> dis = update ? restoreDistance(area) : roadDao
				.selectDistances(area);
		dis.addAll(distanceFromLights(area));
		return Dijkstra.doDijkstra(dis, start, end);
	}

	@Override
	public List<Distance> distanceFromLights(int area) throws DBException {
		List<Intersection> ls = intersectionDao.query(area);
		List<Distance> dis = new ArrayList<Distance>();
		for (Intersection l : ls) {
			if (l.getN1() != null) {
				setLightsRelations(dis, l.getN1(), l);
			}
			if (l.getE1() != null) {
				setLightsRelations(dis, l.getE1(), l);
			}
			if (l.getS1() != null) {
				setLightsRelations(dis, l.getS1(), l);

			}
			if (l.getW1() != null) {
				setLightsRelations(dis, l.getW1(), l);
			}
		}
		return dis;
	}

	public static void setLightsRelations(List<Distance> dis, Point from,
			Intersection l) {
		Distance d = new Distance();
		if (l.getN2() != null) {
			d = new Distance();
			d.setId(0);
			d.setPoint1(from);
			d.setPoint2(l.getN2());
			d.setDistance(BeanUtils.hypot(d.getPoint1(), d.getPoint2()));
			dis.add(d);
		}
		if (l.getE2() != null) {
			d = new Distance();
			d.setId(0);
			d.setPoint1(from);
			d.setPoint2(l.getE2());
			d.setDistance(BeanUtils.hypot(d.getPoint1(), d.getPoint2()));
			dis.add(d);
		}
		if (l.getS2() != null) {
			d = new Distance();
			d.setId(0);
			d.setPoint1(from);
			d.setPoint2(l.getS2());
			d.setDistance(BeanUtils.hypot(d.getPoint1(), d.getPoint2()));
			dis.add(d);
		}
		if (l.getW2() != null) {
			d = new Distance();
			d.setId(0);
			d.setPoint1(from);
			d.setPoint2(l.getW2());
			d.setDistance(BeanUtils.hypot(d.getPoint1(), d.getPoint2()));
			dis.add(d);
		}
	}

	public Graph getGraph() {
		return graph;
	}

	public void setGraph(Graph graph) {
		this.graph = graph;
	}

	public RoadDao getRoadDao() {
		return roadDao;
	}

	public void setRoadDao(RoadDao roadDao) {
		this.roadDao = roadDao;
	}

	@Override
	public List<Vehicle> getCurrentVehicleMovement(int area) throws DBException {
		if (randomMode.modeStatus() == VehicleMode.NOT_INITIALIZED) {
			randomMode.setAREA(area);
			randomMode.getCurrentThread().start();
		} else if (randomMode.modeStatus() == VehicleMode.STOP) {
			Vehicle.ves = null;
			randomMode = new RandomVehicleMode();
			randomMode.setRoadService(this);
			randomMode.setStaticData(staticData);
			randomMode.setAREA(area);
			randomMode.getCurrentThread().start();
		}		     
		return Vehicle.ves;
	}
	
	@Override
	public List<RoadStatus> calculateRoadStatus(int area) throws DBException{
		if(roadStatus==null){
			roadStatus=new CalculateRoadStatusImpl();
		}
		if (randomMode.modeStatus() == VehicleMode.RUNNING&&roadStatus.modeStatus()!=CalculateRoadStatus.RUNNING) {
			RoadStatus.rss=null;
			roadStatus.setArea(area);
			roadStatus.setStaticData(staticData);
			roadStatus.getCurrentThread().start();
		}
		return RoadStatus.rss;
	}

	public VehicleMode getRandomMode() {
		return randomMode;
	}

	public void setRandomMode(VehicleMode rm) {
		randomMode = rm;
	}

	public StaticData getStaticData() {
		return staticData;
	}

	public void setStaticData(StaticData staticData) {
		this.staticData = staticData;
	}

	public IntersectionDao getIntersectionDao() {
		return intersectionDao;
	}

	public void setIntersectionDao(IntersectionDao intersectionDao) {
		this.intersectionDao = intersectionDao;
	}

	@Override
	public List<Tile> getAllTiles() throws DBException {
		return tileDao.selectAll();
	}

	public TileDao getTileDao() {
		return tileDao;
	}

	public void setTileDao(TileDao tileDao) {
		this.tileDao = tileDao;
	}
	
	public  CalculateRoadStatus getRoadStatus() {
		return roadStatus;
	}

	public void setRoadStatus(CalculateRoadStatus rs) {
		roadStatus = rs;
	}
	
	@Override
	public boolean saveOrUpdateTiles(List<Tile> tiles) throws DBException {
		return tileDao.save(tiles);
	}

	@Override
	public List<Vehicle> circleFilter(List<Vehicle> ves, int id)
			throws DBException {
		if (ves == null)
			return null;
		Vehicle center = null;
		for (Vehicle v : ves) {
			if (v.getId() == id) {
				center = v;
				break;
			}
		}
		if (center == null)
			return ves;
		List<Vehicle> result = new ArrayList<Vehicle>();
		for (Vehicle v : ves) {
			if (BeanUtils.hypot(v.getCurrent(), center.getCurrent()) <= RADIUS)
				result.add(v);
		}
		return result;
	}

}
